use crate::action::user::{create_user, CreateError};
use crate::data::message::Message;
use crate::fairing::user::UserDb;
use crate::guard::userauth::AdminUserData;
use crate::model::user::{UserData, UserInfo, Username};
use rocket::http::Status;
use rocket::request::Request;
use rocket::response::{self, Responder, Response};
use rocket::route::Route;
use rocket::serde::json::Json;
use rocket_db_pools::{
    sqlx::{self, Acquire},
    Connection,
};
use serde::Deserialize;

enum CreateUserStatus {
    Ok(UserData),
    ExistingUsername,
    Failed(sqlx::Error),
    InvalidUsername,
}

impl<'r> Responder<'r, 'static> for CreateUserStatus {
    fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
        match self {
            CreateUserStatus::Ok(user) => Response::build_from(Json(user).respond_to(req)?)
                .status(Status::Created)
                .ok(),
            CreateUserStatus::ExistingUsername => Response::build_from(
                Json(Message {
                    message: String::from("已存在用户名相同的用户"),
                })
                .respond_to(req)?,
            )
            .status(Status::PreconditionFailed)
            .ok(),
            CreateUserStatus::Failed(err) => Response::build_from(
                Json(Message {
                    message: err.to_string(),
                })
                .respond_to(req)?,
            )
            .status(Status::InternalServerError)
            .ok(),
            CreateUserStatus::InvalidUsername => Response::build_from(
                Json(Message {
                    message: String::from("用户名为空"),
                })
                .respond_to(req)?,
            )
            .status(Status::PreconditionFailed)
            .ok(),
        }
    }
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct NewUserInfo {
    username: Username,
    is_admin: bool,
}

#[post("/create", data = "<user_info>")]
async fn create<'r>(
    mut db: Connection<UserDb>,
    _user: AdminUserData,
    user_info: Json<NewUserInfo>,
) -> CreateUserStatus {
    let result = async {
        let mut tx = db.begin().await?;

        let new_user = create_user(
            &mut tx,
            user_info.0.username,
            UserInfo {
                is_admin: user_info.0.is_admin,
            },
        )
        .await?;

        tx.commit().await?;

        Ok(new_user)
    }
    .await;
    match result {
        Ok(user) => CreateUserStatus::Ok(user),
        Err(err) => match err {
            CreateError::ExistingUsername => CreateUserStatus::ExistingUsername,
            CreateError::DatabaseError(err) => CreateUserStatus::Failed(err),
            CreateError::InvalidUsername => CreateUserStatus::InvalidUsername,
        },
    }
}

pub fn route() -> Vec<Route> {
    routes![create]
}
